package com.siashan.toolkit.crypt.asymmetric;

import com.siashan.toolkit.crypt.CryptException;
import com.siashan.toolkit.crypt.EncodeType;
import com.siashan.toolkit.crypt.KeyUtil;
import com.siashan.toolkit.crypt.SecureUtil;

import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;

/**
 * 签名包装，{@link Signature} 包装类
 * 
 * @author siashan
 * @since 1.0.7
 */
public class SignUtil {

// ----------------------------------------------------- Sign   ------------------------------------------------------//

	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param privateKey   私钥
	 * @return 签名
	 */
	public static byte[] sign(String algorithm,byte[] data,PrivateKey privateKey) {
		try {
			Signature signature = Signature.getInstance(algorithm);
			signature.initSign(privateKey);
			signature.update(data);
			return signature.sign();
		} catch (Exception e) {
			throw new CryptException(e);
		}
	}
	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param privateKey   私钥
	 * @return 签名
	 */
	public static byte[] sign(String algorithm,byte[] data,byte[] privateKey) {
		PrivateKey key = SecureUtil.generatePrivateKey(algorithm, privateKey);
		return sign(algorithm,data,key);
	}

	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param privateKey   私钥
	 * @return 签名
	 */
	public static byte[] sign(String algorithm,byte[] data,String privateKey) {
		return sign(algorithm,data, KeyUtil.decodeKey(privateKey, EncodeType.BASE64));
	}

	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param privateKey   私钥
	 * @param encodeType   秘钥加密类型
	 * @return 签名
	 */
	public static byte[] sign(String algorithm,byte[] data,String privateKey,EncodeType encodeType) {
		return sign(algorithm,data, KeyUtil.decodeKey(privateKey, encodeType));
	}

	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param privateKey   私钥
	 * @return 签名
	 */
	public static byte[] sign(SignAlgorithm algorithm,byte[] data,byte[] privateKey) {
		return sign(algorithm.getValue(),data,privateKey);
	}

	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param privateKey   私钥
	 * @return 签名
	 */
	public static byte[] sign(SignAlgorithm algorithm,String data,byte[] privateKey) {
		return sign(algorithm.getValue(),data.getBytes(StandardCharsets.UTF_8),privateKey);
	}

	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param privateKey   私钥
	 * @return 签名
	 */
	public static byte[] sign(SignAlgorithm algorithm,String data,String privateKey) {
		return sign(algorithm.getValue(),data.getBytes(StandardCharsets.UTF_8),privateKey);
	}


	/**
	 * 用私钥对信息生成数字签名
	 *
	 * @param algorithm    签名算法
	 * @param data         加密数据
	 * @param keyPair      秘钥对
	 * @return 签名
	 */
	public static byte[] sign(String algorithm,byte[] data,KeyPair keyPair) {
		return sign(algorithm,data,keyPair.getPrivate());
	}

// -----------------------------------------------------  Verify  ----------------------------------------------------//
	/**
	 * 用公钥检验数字签名的合法性
	 *
	 * @param algorithm   签名算法
	 * @param data 数据
	 * @param sign 签名
	 * @param publicKey   公钥
	 * @return 是否验证通过
	 */
	public static boolean verify(String algorithm,byte[] data, byte[] sign,PublicKey publicKey) {
		try {
			Signature signature = Signature.getInstance(algorithm);
			signature.initVerify(publicKey);
			signature.update(data);
			return signature.verify(sign);
		} catch (Exception e) {
			throw new CryptException(e);
		}
	}

	/**
	 * 用公钥检验数字签名的合法性
	 *
	 * @param algorithm   签名算法
	 * @param data 数据
	 * @param sign 签名
	 * @param publicKey   公钥
	 * @return 是否验证通过
	 */
	public static boolean verify(String algorithm,byte[] data, byte[] sign,byte[] publicKey) {
		PublicKey key = SecureUtil.generatePublicKey(algorithm, publicKey);
		return verify(algorithm,data,sign,key);
	}

	/**
	 * 用公钥检验数字签名的合法性
	 *
	 * @param algorithm   签名算法
	 * @param data 数据
	 * @param sign 签名
	 * @param publicKey   公钥
	 * @return 是否验证通过
	 */
	public static boolean verify(String algorithm,byte[] data, byte[] sign,String publicKey) {
		return verify(algorithm,data,sign,KeyUtil.decodeKey(publicKey,EncodeType.BASE64));
	}

	/**
	 * 用公钥检验数字签名的合法性
	 *
	 * @param algorithm   签名算法
	 * @param data 数据
	 * @param sign 签名
	 * @param publicKey   公钥
	 * @return 是否验证通过
	 */
	public static boolean verify(SignAlgorithm algorithm,byte[] data, byte[] sign,String publicKey) {
		return verify(algorithm.getValue(),data,sign,KeyUtil.decodeKey(publicKey,EncodeType.BASE64));
	}

	/**
	 * 用公钥检验数字签名的合法性
	 *
	 * @param algorithm   签名算法
	 * @param data 数据
	 * @param sign 签名
	 * @param publicKey   公钥
	 * @return 是否验证通过
	 */
	public static boolean verify(SignAlgorithm algorithm,byte[] data, byte[] sign,byte[] publicKey) {
		return verify(algorithm.getValue(),data,sign,publicKey);
	}



	/**
	 * 用公钥检验数字签名的合法性
	 *
	 * @param algorithm   签名算法
	 * @param data 数据
	 * @param sign 签名
	 * @param keyPair     秘钥对
	 * @return 是否验证通过
	 */
	public static boolean verify(String algorithm,byte[] data, byte[] sign,KeyPair keyPair) {
		return verify(algorithm,data,sign,keyPair.getPublic());
	}
}
